package com.xcity.game.player;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xcity.Server;
import com.xcity.config.ConfigEx;
import com.xcity.db.entity.Player;
import com.xcity.db.entity.User;
import com.xcity.db.mapper.PlayerMapper;
import com.xcity.game.common.AbstractConfirmOperation;
import com.xcity.game.common.Cause;
import com.xcity.game.common.ConfirmOperation;
import com.xcity.game.common.GameStaticConfig;
import com.xcity.game.common.MessageConst;
import com.xcity.game.common.PlayerPool;
import com.xcity.game.event.GameEvents;
import com.xcity.game.map.MapService;
import com.xcity.game.pet.Pet;
import com.xcity.game.player.cache.PlayerCacheService;
import com.xcity.game.relation.RelationService;
import com.xcity.game.treasurebox.TreasureBoxState;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.push.NoticePush;
import com.xcity.pomelo.push.SyncPush;
import com.xcity.pomelo.push.msg.BuffsPush;
import com.xcity.pomelo.push.msg.TipPush;
import com.xcity.pomelo.push.msg.TreasureBoxPush;
import com.xcity.pomelo.push.sync.MapSync;
import com.xcity.pomelo.push.sync.StringSync;
import com.xcity.pomelo.request.ConfirmRequest;
import com.xcity.pomelo.request.Get10OppositeSexRoleRequest;
import com.xcity.pomelo.request.IPlayerCreateRequest;
import com.xcity.pomelo.request.PayFineRequest;
import com.xcity.pomelo.request.PlayerCreateRequest;
import com.xcity.pomelo.request.PlayerLoadRequest;
import com.xcity.pomelo.request.PlayerModifyRequest;
import com.xcity.pomelo.request.SetSignRequest;
import com.xcity.pomelo.request.SystemSettingsRequest;
import com.xcity.pomelo.request.UpdateGuideRequest;
import com.xcity.pomelo.response.HintResponse;
import com.xcity.pomelo.response.OppositeSexPlayerResponse;
import com.xcity.pomelo.response.PlayerGuideResponse;
import com.xcity.pomelo.response.PlayerLoadResponse;
import com.xcity.pomelo.response.PlayerMoneyResponse;
import com.xcity.pomelo.response.PlayerSignResponse;
import com.xcity.pomelo.response.PlayerSimpleInfoResponse;
import com.xcity.pomelo.response.Response;
import com.xcity.pomelo.response.SystemSettingsResponse;
import com.xcity.util.DBUtils;
import com.xcity.util.IdUtils;
import com.xcity.util.RegionUtils;
import com.xcity.util.RegionUtils.City;
import com.xcity.util.SessionUtils;

import gnu.trove.map.TLongObjectMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import naga.x.App;
import naga.x.common.Updatable;
import naga.x.event.Event;
import naga.x.event.EventListener;
import naga.x.event.Events;
import naga.x.game.time.DayListener;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.Service;
import naga.x.service.ServiceException;
import naga.x.util.CommonUtils;
import naga.x.util.KeywordsManager;
import naga.x.util.StringUtils;

@OPHandler
public class PlayerService implements Service, EventListener, Updatable, DayListener {

	protected static final Logger LOG = LoggerFactory.getLogger(PlayerService.class);
	
	public static final int NAME_MAX_LEN = 8;
	protected Map<Long, Player> players = new ConcurrentHashMap<Long, Player>(256); // 所有在线玩家
//	protected TLongObjectMap<Player> pendingLogoutPlayers = new TLongObjectHashMap<Player>(); // 下线的玩家,准备保存
	protected Map<Long, Player> pendingLogoutPlayers = new ConcurrentHashMap<Long, Player>(64);
	protected long lastSyncLogoutPlayersTime;
	protected Updatable syncLogoutPlayers = new Updatable() {
		@Override
		public boolean update() {
			int size = pendingLogoutPlayers.size();
			if ((TimeUpdater.getInstance().now() - lastSyncLogoutPlayersTime < 5000)) {
				// 每5秒存一次下线玩家
				return false;
			}
			lastSyncLogoutPlayersTime = TimeUpdater.getInstance().now();
			if (size == 0) {
				return false;
			}
			Iterator<Entry<Long, Player>> itr = pendingLogoutPlayers.entrySet().iterator();
			while (itr.hasNext()) {
				Player player = itr.next().getValue();
				try {
					player.save();
					itr.remove();
					LOG.info("[PLAYER SAVE]ID[{}]NAME[{}]LVL[{}]EXP[{}]GOLD[{}]STONE[{}]LOGOUT", player.getId(), player.getName(), player.getLevel(), player.getExp(), player.getGold(), player.getStone());
				} catch (Exception e) {
					LOG.error("[PLAYER SAVE ERROR]ID[" + player.getId()
						+ "]NAME[" + player.getName()
						+ "]LVL[" + player.getLevel()
						+ "]EXP[" + player.getExp()
						+ "]GOLD[" + player.getGold()
						+ "]STONE[" + player.getStone()
						+ "]LOGOUT", e);
				}
			}
			return true;
		}
	};
	protected long lastPrintOnlinePlayerSizeTime = 0;
	protected TLongObjectMap<String> offlineMsgs = new TLongObjectHashMap<String>(); // 存储离线玩家的重要的提示消息，玩家上线后提示(如：离线充值)
	
	protected KeywordsManager badwordsFilter;
	protected MapService mapService;
	protected PlayerCacheService cacheService;
	
	@Override
	public void startup() throws ServiceException {
		App.getApp().getEventManager().register(this);
		App.getApp().getUpdater().add(this);
		App.getApp().getUpdater().addAsync(syncLogoutPlayers);
		App.getApp().getSchedule().scheduleWithFixedDelay(new SaveOnlinePlayersOperation(), 1, 5, TimeUnit.MINUTES);
		TimeUpdater.getInstance().addDayListener(this);
		badwordsFilter = ((ConfigEx) App.getApp().getConfig()).getBadwordsFilter();
		
		Player.VIGOUR_RECOVER_VALUE = GameStaticConfig.addVigourValue;
		Player.VIGOUR_RECOVER_TIME_INTERVAL = GameStaticConfig.addVigourGap * 1000;
		Player.VIGOUR_RESTORE_VALUE = GameStaticConfig.restoreVigour;
		Player.STRENGTH_RESTORE_VALUE = GameStaticConfig.restoreStrength;
		Player.MONEY_RESTORE_VALUE = GameStaticConfig.restoreMoney;
	}

	@Override
	public void shutdown() throws ServiceException {
		saveAll(players.values());
		saveAll(pendingLogoutPlayers.values());
	}
	
	private void saveAll(Collection<Player> vals) {
		for (Player player : vals) {
			try {
				if (player.getLastLogoutTime() == null) {
					player.setLastLogoutTime(new Date());
				}
				player.save();
				LOG.info("[PLAYER SAVE]ID[{}]NAME[{}]LVL[{}]EXP[{}]GOLD[{}]STONE[{}]SHUTDOWN", player.getId(), player.getName(), player.getLevel(), player.getExp(), player.getGold(), player.getStone());
			} catch (Exception e) {
				LOG.error("[PLAYER SAVE ERROR]ID[" + player.getId()
										+ "]NAME[" + player.getName()
										+ "]LVL[" + player.getLevel()
										+ "]EXP[" + player.getExp()
										+ "]GOLD[" + player.getGold()
										+ "]STONE[" + player.getStone()
										+ "]SHUTDOWN", e);
			}
		}
	}

	@Override
	public String getId() {
		return getClass().getName();
	}
	
	@Override
	public int[] getEventTypes() {
		return new int[]{ Events.EVENT_SESSION_CLOSED };
	}

	@Override
	public void handleEvent(Event event) {
		switch (event.getType()) {
			case Events.EVENT_SESSION_CLOSED:
				Session sess = event.getParameter();
				Boolean kicked = (Boolean) sess.getAttribute(SessionUtils.KICKED);
				if (kicked != null && kicked) {
					return;
				}
				Player player = sess.getClient();
				playerLogout(player);
				break;
		}
	}
	
	/**
	 * 角色登录
	 * @param session
	 * @param req
	 */
	@PomeloOP(PomeloRoute.PLAYER_LOAD)
	protected void playerLoad(Session session, PlayerLoadRequest req) {
		if (Server.getInstance().hasState(Server.STATE_LOGIN_BAN_MASK)) {
			session.send(new HintResponse(req.getSerial(), MessageConst.REGISTER_CLOSED));
			return;
		}
		User acc;
		if ((acc = (User) session.getAttribute(SessionUtils.ACCOUNT)) == null) { // 未登录
			session.send(new HintResponse(req.getSerial(), MessageConst.NO_LOGIN));
			return;
		}
		boolean register = false;
		Player player = loadPlayer(acc.getDefaultPlayerId());
		if (player == null) {
			try {
				player = playerRegister(acc, req);
				register = true;
			} catch (PlayerException e) {
				session.send(new HintResponse(req.getSerial(), e.getMessage()));
				return;
			}
		}
		beforeLogin(player);
		if (StringUtils.isNotEmpty(req.getHeadimgurl())) {
			player.setIcon(PlayerUtils.mappingIcon(acc.getChannel(), req.getHeadimgurl(), player.getSex()));
		}
		player.setLastLoginTime(new Date());
		player.setAccount(acc);
		session.setClient(player);
		App.getApp().getEventManager().fireEvent(Events.EVENT_PLAYER_LOGIN, player);
		session.send(new PlayerLoadResponse(req.getSerial(), player, register));
		afterLogin(player);
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_PLAYER_LOADED, player);
		LOG.info("[LOGIN]ID[{}]NAME[{}]LVL[{}]EXP[{}]GOLD[{}]STONE[{}]", player.getId(), player.getName(), player.getLevel(), player.getExp(), player.getGold(), player.getStone());
	}
	
	protected void beforeLogin(Player player) {
		// DO NOTHING
	}
	
	protected void afterLogin(Player player) {
		player.send(new BuffsPush(player.getBuffs().getBuffs()));
		Pet pet = player.getPets().getWorkingPet();
		if (pet != null) {
			player.send(new StringSync(SyncPush.SYNC_WATCHDOG, pet.getTemplate().getAnimation()));
			player.send(new MapSync(SyncPush.SYNC_PET_SPIRIT, pet.getId(), new int[] { pet.getSpirit(), pet.getTemplate().getSpirit() }));
		}
		String offlineMsg = removeOfflineMsg(player.getId());
		if (StringUtils.isNotEmpty(offlineMsg)) {
			player.send(new TipPush(offlineMsg));
		}
		if (player.getPool().getObject(PlayerPool.PROPERTY_TREASURE_BOX_STATE, TreasureBoxState.class) == TreasureBoxState.INITED) {
			player.send(TreasureBoxPush.DEFAULT);
		}
	}
	
	/**
	 * 新建角色
	 * @param session
	 */
	@PomeloOP(PomeloRoute.PLAYER_CREATE)
	protected void playerCreate(Session session, PlayerCreateRequest req) {
		User acc;
		if ((acc = (User) session.getAttribute(SessionUtils.ACCOUNT)) == null) { // 未登录
			session.send(new HintResponse(req.getSerial(), MessageConst.NO_LOGIN));
			return;
		}
		if (acc.getDefaultPlayerId() != 0 || session.getClient() != null) {
			session.send(new HintResponse(req.getSerial(), MessageConst.PLAYER_CREATE_DUPLICATE));
			return;
		}
		try {
			Player player = playerRegister(acc, req);
//			player.setAccount(acc);
			session.setClient(player);
			session.send(new PlayerLoadResponse(req.getSerial(), player, true));
		} catch (PlayerException e) {
			session.send(new HintResponse(req.getSerial(), e.getMessage()));
		}
	}
	
	static class ChangeNameOperation extends AbstractConfirmOperation {

		String name;
		int stone;
		
		public ChangeNameOperation(Player ref, String name, int stone, String msg) {
			super(ref, msg);
			this.name = name;
			this.stone = stone;
		}

		@Override
		public void ok(Player player) {
			if (!player.costStone(stone, Cause.MODIFY_NAME)) {
				player.send(new TipPush(MessageConst.STONE_NOT_ENOUTH));
				return;
			}
			int newTimes = player.poolAdd(PlayerPool.PROPERTY_INFO_MODIFY_TIMES, 1);
			player.setName(name);
			player.send(new TipPush(MessageConst.MODIFY_SUCC));
			player.send(new StringSync(SyncPush.SYNC_NAME, name));
			App.getApp().getEventManager().addEvent(GameEvents.EVENT_CHANGE_NAME, player);
			LOG.info("[CHGNAME]ID[{}]NAME[{}]SEX[{}]ICON[{}]TIMES[{}]", player.getId(), player.getName(), player.getSex(), player.getIcon(), newTimes);
		}
		
	}
	
	@PomeloOP(PomeloRoute.PLAYER_MODIFY)
	protected void playerModify(Session session, PlayerModifyRequest req) {
		Player player = session.getClient();
		try {
			String name = filterPlayerName(req.getName(), true);
			int times = player.getPool().getIntValue(PlayerPool.PROPERTY_INFO_MODIFY_TIMES);
			if (times > 0) {
				if (name.equals(player.getName())) {
					session.send(new HintResponse(req.getSerial(), MessageConst.PLAYER_NAME_DUPLICATE));
					return;
				}
				int cost = (int) (30 * Math.pow(2.5, times - 1));
				ChangeNameOperation op = new ChangeNameOperation(player, name, cost, MessageFormat.format(MessageConst.CHANGE_NAME_USE_STONE_TIP, cost));
				op.send();
				session.send(new Response(req.getSerial()));
				return;
			}
			int newTimes = player.poolAdd(PlayerPool.PROPERTY_INFO_MODIFY_TIMES, 1);
			player.setName(name);
			player.setSex(req.getSex());
			String icon = PlayerUtils.mappingIcon(player.getAccount().getChannel(), req.getIcon(), player.getSex());
			player.setIcon(icon);
			session.send(new PlayerSimpleInfoResponse(req.getSerial(), player));
			App.getApp().getEventManager().addEvent(GameEvents.EVENT_CHANGE_NAME, player);
			LOG.info("[CHGNAME]ID[{}]NAME[{}]SEX[{}]ICON[{}]TIMES[{}]", player.getId(), player.getName(), player.getSex(), player.getIcon(), newTimes);
		} catch (PlayerException e) {
			session.send(new HintResponse(req.getSerial(), e.getMessage()));
		}
	}
	
	private String filterPlayerName(String name, boolean throwsException) throws PlayerException {
		String n = StringUtils.trim(name);
		if (n.isEmpty()) {
			if (throwsException)
				throw new PlayerException(MessageConst.PLAYER_NAME_EMPTY);
			return MessageConst.ACCOUNT_VISITOR + IdUtils.nextTempId();
		}
		if (n.length() > NAME_MAX_LEN) {
			if (throwsException)
				throw new PlayerException(MessageFormat.format(MessageConst.PLAYER_NAME_LEN_EXCEED, NAME_MAX_LEN));
			return n.substring(0, NAME_MAX_LEN);
		}
		if (badwordsFilter != null && badwordsFilter.containsBadWord(n)) {
			if (throwsException)
				throw new PlayerException(MessageConst.PLAYER_NAME_INVALID);
			return badwordsFilter.filterBadWords(n);
		}
		return n;
	}
	
	public Player loadPlayer(long playerId) {
		if (playerId == 0) {
			return null;
		}
		// 1. online
		Player player = players.get(playerId);
		if (player != null) {
			return player;
		}
		// 2. pending logout
		player = pendingLogoutPlayers.remove(playerId);
		if (player != null) {
			players.put(playerId, player);
			return player;
		}
		// 3. cache
		player = getCacheService().remove(playerId);
		if (player != null) {
			players.put(playerId, player);
			return player;
		}
		// 4. database
		player = DBUtils.find(playerId, PlayerMapper.class);
		if (player != null) {
			player.init();
			players.put(playerId, player);
		}
		return player;
	}
	
	protected Player playerRegister(User user, IPlayerCreateRequest req) throws PlayerException {
		if (Server.getInstance().hasState(Server.STATE_REGISTER_BAN_MASK)) {
			throw new PlayerException(MessageConst.REGISTER_CLOSED);
		}
		String name = filterPlayerName(StringUtils.isEmpty(req.getName()) ? user.getNickname() : req.getName(), false);
		Player player = new Player(user.getId(), name, req.getIcon(), req.getSex());
		player.setAccount(user);
		City city = RegionUtils.find(req.getCity());
		player.setProvince(city.province.name);
		player.setCity(city.name);
		player.setGold(GameStaticConfig.initGold);
		player.setStone(GameStaticConfig.initStone);
		
		// 出生坐标
		int[] pos = new int[]{-1, 0, 0};//getMapService().nextPosition();
		player.setMap(pos[0]);
		player.setX(pos[1]);
		player.setY(pos[2]);
		player.init();
//		player.getBag().addItemWithoutException(EmployeeRecruitConfigure.Type.GENERAL_RESUME.val, 1, Cause.FRESHER);
//		player.getBag().addItemWithoutException(EmployeeRecruitConfigure.Type.SENIOR_RESUME.val, 1, Cause.FRESHER);
//		player.getBag().addItemWithoutException(SkillService.SKILL_BOOK_ID_FOR_FRESHER, 1, Cause.FRESHER); // 20170331 removed, no use
//		player.getSkillManager().addSkillWithoutException(SkillService.playerDefaultSkillTemplate, Cause.FRESHER);
		player.getBag().addItemWithoutException(30048, 20, Cause.FRESHER);
		player.getBag().addItemWithoutException(30049, 20, Cause.FRESHER);
		player.getBag().addItemWithoutException(30050, 20, Cause.FRESHER);
		player.getBag().addItemWithoutException(30051, 20, Cause.FRESHER);
		player.getBag().addItemWithoutException(30052, 20, Cause.FRESHER);
		DBUtils.save(player, PlayerMapper.class);
		
		user.addPlayer(player.getId());
		players.put(player.getId(), player);
		App.getApp().getEventManager().addEvent(Events.EVENT_PLAYER_REGISTER, player, req.getInviter());
		LOG.info("[REGISTER]ID[{}]NAME[{}]SEX[{}]UID[{}]CHANNEL[{}]", player.getId(), player.getName(), player.getSex(), user.getId(), user.getChannel());
		return player;
	}
	
	private MapService getMapService() {
		return mapService != null ? mapService : (mapService = App.getApp().getServiceManager().get(MapService.class));
	}
	
	private void playerLogout(Player player) {
		if (player == null) {
			return;
		}
		player.doSyncPool();
		App.getApp().getEventManager().fireEvent(Events.EVENT_PLAYER_LOGOUT, player);
		player.setLastLogoutTime(new Date());
		players.remove(player.getId());
		pendingLogoutPlayers.put(player.getId(), player);
		LOG.info("[LOGOUT]ID[{}]NAME[{}]LVL[{}]EXP[{}]GOLD[{}]STONE[{}]", player.getId(), player.getName(), player.getLevel(), player.getExp(), player.getGold(), player.getStone());
	}

	@Override
	public boolean update() {
		Collection<Player> vals = players.values();
		for (Player player : vals) {
			player.update();
		}
		if (TimeUpdater.getInstance().now() - lastPrintOnlinePlayerSizeTime > 60000) {
			// 每分钟打印一次在线人数
			lastPrintOnlinePlayerSizeTime = TimeUpdater.getInstance().now();
			LOG.info("[ONLINE]" + vals.size());
		}
		return true;
	}
	
//	@PomeloOP(PomeloRoute.VISITORS)
//	protected void requestOppositeSexPlayer5(Session session, VisitorsRequest req) {
//		requestOppositeSexPlayers(session, 5, req.getSerial());
//	}
	
	@PomeloOP(PomeloRoute.GET_10_OPPOSITE_SEX_ROLES)
	protected void requestOppositeSexPlayer10(Session session, Get10OppositeSexRoleRequest req) {
		requestOppositeSexPlayers(session, 10, req.getSerial());
	}
	
	private void requestOppositeSexPlayers(Session session, int num, int serial) {
		if (players.isEmpty()) {
			session.send(new OppositeSexPlayerResponse(serial, Collections.emptyList()));
			return;
		}
		Player player = session.getClient();
		List<Player> results = new ArrayList<Player>(num);
		Collection<Player> vals = players.values();
		int size = vals.size() - 1; // 除去自己
		
		RelationService rs = App.getApp().getServiceManager().get(RelationService.class);
		boolean getAll = size <= num;
		for (Player p : vals) {
			if (p.getId() != player.getId()
					&& !rs.isFriend(player.getId(), p.getId())) {
				if (getAll || CommonUtils.randomInt(size) <= num) {
					results.add(p);
					if (results.size() >= num) {
						break;
					}
				}
			}
		}
		session.send(new OppositeSexPlayerResponse(serial, results));
	}
	
	@PomeloOP(PomeloRoute.PAY_FINE)
	protected void requestPayFine(Session session, PayFineRequest req) {
		Player player = session.getClient();
		int gold = player.getPool().getIntValue(PlayerPool.PROPERTY_FINE_GOLD);
		int stone = player.getPool().getIntValue(PlayerPool.PROPERTY_FINE_STONE);
		if (gold <= 0 && stone <= 0) {
			session.send(new HintResponse(req.getSerial(), MessageConst.NO_NEED_PAY_FINE));
			return;
		}
		boolean succ = false;
		if (req.getType() == 0 && gold > 0) { // 现金
			succ = player.costGold(gold, Cause.SYSTEM_FINE);
		} else if (req.getType() == 1 && stone > 0) {
			succ = player.costStone(stone, Cause.SYSTEM_FINE);
		}
		if (succ) {
			player.poolRemove(PlayerPool.PROPERTY_FINE_GOLD);
			player.poolRemove(PlayerPool.PROPERTY_FINE_STONE);
			session.send(new PlayerMoneyResponse(req.getSerial(), player));
		} else {
			session.send(new HintResponse(req.getSerial(), req.getType() == 0 ? MessageConst.GOLD_NOT_ENOUGH : MessageConst.STONE_NOT_ENOUTH));
		}
	}
	
	@PomeloOP(PomeloRoute.UPDATE_SIGN)
	protected void requestUpdateSign(Session session, SetSignRequest req) {
		Player player = session.getClient();
		player.setSign(req.getSign());
		session.send(new PlayerSignResponse(req.getSerial(), player.getSign()));
	}
	
	@PomeloOP(PomeloRoute.SYNC_GUIDE)
	protected void requestSyncGuide(Session session, UpdateGuideRequest req) {
		Player player = session.getClient();
		short val = player.getPool().getShortValue(PlayerPool.PROPERTY_GUIDE);
		short newVal = val;
		if (val < req.getGuide()) {
			newVal = req.getGuide();
			player.poolSet(PlayerPool.PROPERTY_GUIDE, newVal);
			if (newVal >= GameStaticConfig.fresherMaxGuide) {
				App.getApp().getEventManager().addEvent(GameEvents.EVENT_FRESHER_GUIDE_END, player);
			}
		}
		session.send(new PlayerGuideResponse(req.getSerial(), newVal));
		LOG.info("[SYNC GUIDE]ID[{}]OLD[{}]TRY[{}]VAL[{}]", player.getId(), val, req.getGuide(), newVal);
	}
	
	@PomeloOP(PomeloRoute.CONFIRM_BOX_REQUEST)
	protected void requestConfirm(Session session, ConfirmRequest req) {
		Player player = session.getClient();
		ConfirmOperation op = player.getConfirmOperation();
		if (op == null/* || op.getId() != req.getId() *//*不判断id，同时只可能有一个确认框*/) {
			session.send(new HintResponse(req.getSerial(), MessageConst.OP_TIME_OUT));
			return;
		}
		op.operate(req.getOp());
		session.send(new Response(req.getSerial()));
	}
//	@Deprecated
//	@PomeloOP(PomeloRoute.BUY_STRENGTH)
//	protected void requestBuyStrength(Session session, BuyStrengthRequest req) {
//		Player player = session.getClient();
//		// 每50点体力10钻
////		int str = PlayerUtils.getMaxStrength(player) - player.getStrength();
////		if (str <= 0) {
////			session.send(new HintResponse(req.getSerial(), MessageConst.STRENGTH_FULL_NO_NEED_BUY));
////			return;
////		}
////		if (req.getVal() <= 0) {
////			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
////			return;
////		}
//		int leftBuyCount = PlayerUtils.getLeftBuyStrengthTimes(player);
//		if (leftBuyCount <= 0) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.STRENGTH_BUY_TIMES_MAX));
//			return;
//		}
////		int stone = BuyCountUtils.getTemplate(leftBuyCount).getBuyStrengthStone();//(req.getVal() + 4) / 5;
////		new BuyStrengthOperation(player, req.getVal(), stone).send();
//		int stone = BuyCount.STRENGTH.getCostStone(player);
//		if (!player.costStone(stone, Cause.BUY_STRENGTH)) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.STONE_NOT_ENOUTH));
//			return;
//		}
//		player.addStrength2(Player.STRENGTH_RESTORE_VALUE/*req.getVal()*/, Cause.BUY_STRENGTH);
//		PlayerUtils.countBuyStrength(player, 1);
//		session.send(new BuyStrengthResponse(req.getSerial(), player));
//	}
	
	@PomeloOP(PomeloRoute.SYSTEM_SETTINGS)
	protected void requestUpdateSystemSettings(Session session, SystemSettingsRequest req) {
		if (req.getIndex() < SystemSettingsRequest.MIN_INDEX || req.getIndex() > SystemSettingsRequest.MAX_INDEX) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			return;
		}
		Player player = session.getClient();
		int val = player.getPool().getInt(PlayerPool.PROPERTY_SYSTEM_SETTINGS, SystemSettingsRequest.DEFAULT_VALUE);
		if (req.getVal() == 1) { // on
			int mask = 1 << req.getIndex();
			val |= mask;
		} else { // off
			int mask = ~(1 << req.getIndex());
			val &= mask;
		}
		player.poolSet(PlayerPool.PROPERTY_SYSTEM_SETTINGS, val);
		session.send(new SystemSettingsResponse(req.getSerial(), req.getIndex(), req.getVal()));
	}
	
	/**
	 * 发送公告
	 */
	public void sendNotice(byte id, String content) {
		sendNotice(new NoticePush(id, content));
	}
	
	public void sendNotice(NoticePush notice) {
//		Collection<Player> vals = players.values();
//		for (Player player : vals) {
//			player.send(notice);
//		}
		Server.broadcast(notice);
	}
	
	public Player getOnlinePlayer(long playerId) {
		return players.get(playerId);
	}
	
	public Player getPendingLogoutPlayer(long playerId) {
		return pendingLogoutPlayers.get(playerId);
	}
	
	public Collection<Player> getOnlinePlayers() {
		return players.values();
	}
	
	public void addOfflineMsg(long playerId, String msg) {
		synchronized (offlineMsgs) {
			offlineMsgs.put(playerId, msg);
		}
	}
	
	public String removeOfflineMsg(long playerId) {
		synchronized (offlineMsgs) {
			return offlineMsgs.remove(playerId);
		}
	}

	@Override
	public boolean dayChanged() {
		// 重置所有在线玩家的体力
		Collection<Player> players = getOnlinePlayers();
		for (Player player : players) {
			player.resetStrength(Cause.AUTO);
		}
		return true;
	}
	
	private PlayerCacheService getCacheService() {
		return cacheService != null ? cacheService : (cacheService = App.getApp().getServiceManager().get(PlayerCacheService.class));
	}

}
